/FileExtraction/Java_unseen/Kegbot_kegbot-android/kegtab/src/main/java/org/kegbot/backend/LocalBackend.java
/*
 * Copyright 2003-2020 The Kegbot Project contributors <info@kegbot.org>
 *
 * This file is part of the Kegtab package from the Kegbot project. For
 * more information on Kegtab or Kegbot, see <http://kegbot.org/>.
 *
 * Kegtab is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 2.
 *
 * Kegtab is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with Kegtab. If not, see <http://www.gnu.org/licenses/>.
 */

package org.kegbot.backend;

import android.content.ContentResolver;
import android.content.Context;
import android.database.sqlite.SQLiteException;
import android.provider.MediaStore;
import android.util.Log;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

import org.codehaus.jackson.JsonNode;
import org.kegbot.app.util.KegSizes;
import org.kegbot.app.util.TimeSeries;
import org.kegbot.proto.Api.RecordTemperatureRequest;
import org.kegbot.proto.Models.AuthenticationToken;
import org.kegbot.proto.Models.Beverage;
import org.kegbot.proto.Models.BeverageProducer;
import org.kegbot.proto.Models.Controller;
import org.kegbot.proto.Models.Drink;
import org.kegbot.proto.Models.FlowMeter;
import org.kegbot.proto.Models.FlowToggle;
import org.kegbot.proto.Models.Image;
import org.kegbot.proto.Models.Keg;
import org.kegbot.proto.Models.KegTap;
import org.kegbot.proto.Models.Session;
import org.kegbot.proto.Models.SoundEvent;
import org.kegbot.proto.Models.SystemEvent;
import org.kegbot.proto.Models.ThermoLog;
import org.kegbot.proto.Models.ThermoSensor;
import org.kegbot.proto.Models.User;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.List;

import javax.annotation.Nullable;

/** A {@link Backend} which uses the local filesystem for storage. */
public class LocalBackend implements Backend {

  private static final String TAG = LocalBackend.class.getSimpleName();

  private LocalBackendDbHelper mDb;
  private ContentResolver mContentResolver;

  @Override
  public void start(Context context) {
    mDb = new LocalBackendDbHelper(context);
    mContentResolver = context.getContentResolver();
  }

  @Override
  public KegTap startKeg(KegTap tap, String beerName, String brewerName, String styleName,
      String kegType) throws BackendException {
    if (tap.hasCurrentKeg()) {
      endKeg(tap.getCurrentKeg());
    }

    final double volume = KegSizes.getVolumeMl(kegType);
    final Keg keg = mDb.createOrUpdateKeg(Keg.newBuilder()
        .setId(0)
        .setKegType(kegType)
        .setFullVolumeMl(volume)
        .setRemainingVolumeMl(volume)
        .setServedVolumeMl(0)
        .setSpilledVolumeMl(0)
        .setPercentFull(100.0)
        .setOnline(true)
        .setBeverage(Beverage.newBuilder()
            .setId(0)
            .setBeverageType("beer")
            .setName(beerName)
            .setStyle(styleName)
            .setProducer(BeverageProducer.newBuilder()
                .setId(0)
                .setName(brewerName)
                .build())
            .build())
        .build());

    Log.d(TAG, "Created keg: " + keg);

    final KegTap updatedTap = mDb.createOrUpdateTap(KegTap.newBuilder(tap)
        .setCurrentKegId(keg.getId())
        .build());

    Log.d(TAG, "Updated tap: " + updatedTap);

    return updatedTap;
  }

  @Override
  public AuthenticationToken assignToken(String authDevice, String tokenValue, String username)
      throws BackendException {
    throw new OperationNotSupportedException("Local backend does not support users.");
  }

  @Override
  public Image attachPictureToDrink(int drinkId, File picture) throws BackendException {
    throw new OperationNotSupportedException("Local backend does not support photos.");
  }

  @Override
  public User createUser(String username, String email, String password, String imagePath)
      throws BackendException {
    throw new OperationNotSupportedException("Local backend does not support users.");
  }

  @Override
  public Keg endKeg(Keg keg) throws BackendException {
    final int kegId = keg.getId();

    Log.i(TAG, "Taking keg " + kegId + " offline");
    final Keg newKeg = Keg.newBuilder(keg)
        .setOnline(false)
        .build();

    final Keg result;
    try {
      result = mDb.createOrUpdateKeg(newKeg);
    } catch (SQLiteException e) {
      Log.w(TAG, "SQLiteException reading keg " + kegId);
      throw new BackendException(e);
    }

    for (final KegTap tap : getTaps()) {
      if (tap.getCurrentKegId() == keg.getId()) {
        mDb.createOrUpdateTap(KegTap.newBuilder(tap)
            .setCurrentKegId(0)
            .build());
        break;
      }
    }

    return result;
  }

  @Override
  public AuthenticationToken getAuthToken(String authDevice, String tokenValue)
      throws BackendException {
    return null;  // Not Implemented
  }

  @Override
  public Session getCurrentSession() throws BackendException {
    return null;
  }

  @Override
  public List<SystemEvent> getEvents() throws BackendException {
    return Collections.emptyList();
  }

  @Override
  public List<SystemEvent> getEventsSince(long sinceExventId) throws BackendException {
    return Collections.emptyList();
  }

  @Override
  public JsonNode getSessionStats(int sessionId) throws BackendException {
    return null;
  }

  @Override
  public List<SoundEvent> getSoundEvents() throws BackendException {
    return Collections.emptyList();
  }

  @Override
  public List<KegTap> getTaps() throws BackendException {
    return mDb.getAllTaps();
  }

  @Override
  public KegTap createTap(String tapName) throws BackendException {
    final KegTap tap = KegTap.newBuilder()
        .setId(0)
        .setName(tapName)
        .build();
    try {
      return mDb.createOrUpdateTap(tap);
    } catch (SQLiteException e) {
      throw new BackendException("Error updating tap", e);
    }
  }

  @Override
  public void deleteTap(KegTap tap) throws BackendException {
    boolean result = mDb.deleteTap(tap);
    Log.d(TAG, "Deleted row result: " + result);
  }

  @Override
  public User getUser(String username) throws BackendException {
    throw new NotFoundException("Local backend does not support users");
  }

  @Override
  public List<User> getUsers() throws BackendException {
    return Collections.emptyList();
  }

  @Override
  public Drink recordDrink(String tapName, long volumeMl, long ticks, @Nullable String shout,
      @Nullable String username, @Nullable String recordDate, long durationMillis,
      @Nullable TimeSeries timeSeries, @Nullable File picture) throws BackendException {
    final Drink drink;

    String pictureUrl = "";
    if (picture != null) {
      final String imagePath = picture.getAbsolutePath();
      Log.d(TAG, "Storing image, path=" + imagePath + " exists=" + picture.exists());

      try {
        pictureUrl = MediaStore.Images.Media.insertImage(mContentResolver, imagePath,
            picture.getName(), "Kegbot drink snapshot");
      } catch (FileNotFoundException e) {
        Log.w(TAG, "Storing image '" + imagePath + "' failed: " + e);
      }
    }
    try {
      drink = mDb.recordDrink(tapName, volumeMl, ticks, shout, username, recordDate, durationMillis,
          timeSeries, Strings.nullToEmpty(pictureUrl));
    } catch (SQLiteException e) {
      throw new BackendException("Error recording drink", e);
    }

    return drink;
  }

  @Override
  public ThermoLog recordTemperature(RecordTemperatureRequest request) throws BackendException {
    // Ignored.
    return null;
  }

  @Override
  public FlowMeter calibrateMeter(FlowMeter meter, double ticksPerMl) throws BackendException {
    final FlowMeter newMeter = FlowMeter.newBuilder(meter)
        .setTicksPerMl(ticksPerMl)
        .build();
    try {
      return mDb.createOrUpdateFlowMeter(newMeter);
    } catch (SQLiteException e) {
      throw new BackendException("Error updating meter", e);
    }
  }

  @Override
  public Controller createController(String name, String serialNumber, String deviceType) {
    Preconditions.checkNotNull(name);
    serialNumber = Strings.nullToEmpty(serialNumber);
    deviceType = Strings.nullToEmpty(deviceType);
    final Controller controller = Controller.newBuilder()
        .setId(0)
        .setName(name)
        .setSerialNumber(serialNumber)
        .setModelName(deviceType)
        .build();
    return mDb.createOrUpdateController(controller);
  }

  @Override
  public List<Controller> getControllers() throws BackendException {
    return mDb.getAllControllers();
  }

  @Override
  public Controller updateController(Controller controller) throws BackendException {
    return mDb.createOrUpdateController(controller);
  }

  @Override
  public FlowMeter createFlowMeter(Controller controller, String portName, double ticksPerMl)
      throws BackendException {
    final FlowMeter meter = FlowMeter.newBuilder()
        .setId(0)
        .setController(controller)
        .setPortName(portName)
        .setName(String.format("%s.%s", controller.getName(), portName))
        .setTicksPerMl((float) ticksPerMl)
        .build();
    return mDb.createOrUpdateFlowMeter(meter);
  }

  @Override
  public List<FlowMeter> getFlowMeters() throws BackendException {
    return mDb.getAllFlowMeters();
  }

  @Override
  public FlowMeter updateFlowMeter(FlowMeter flowMeter) throws BackendException {
    return mDb.createOrUpdateFlowMeter(flowMeter);
  }

  @Override
  public KegTap connectMeter(KegTap tap, FlowMeter meter) {
    return mDb.connectTapToMeter(tap, meter);
  }

  @Override
  public KegTap disconnectMeter(KegTap tap) {
    return mDb.connectTapToMeter(tap, null);
  }

  @Override
  public List<FlowToggle> getFlowToggles() throws BackendException {
    return mDb.getAllFlowToggles();
  }

  @Override
  public List<ThermoSensor> getThermoSensors() throws BackendException {
    // TODO
    return null;
  }

  @Override
  public KegTap connectThermo(KegTap tap, ThermoSensor thermo) throws BackendException {
    // TODO
    return null;
  }

  @Override
  public KegTap disconnectThermo(KegTap tap) throws BackendException {
    // TODO
    return null;
  }

  @Override
  public FlowToggle updateFlowToggle(FlowToggle flowToggle) throws BackendException {
    return mDb.createOrUpdateFlowToggle(flowToggle);
  }

  @Override
  public KegTap connectToggle(KegTap tap, FlowToggle flowToggle) throws BackendException {
    return mDb.connectTapToToggle(tap, flowToggle);
  }

  @Override
  public KegTap disconnectToggle(KegTap tap) throws BackendException {
    return mDb.connectTapToToggle(tap, null);
  }
}
