package com.orhanobut.hawk;

import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;

import com.google.gson.Gson;

/**
 * @author Orhan Obut
 */
public class HawkBuilder {

  /**
   * never ever change this value since it will break backward compatibility in terms of keeping previous data
   */
  private static final String TAG = "HAWK";

  /**
   * never ever change this value since it will break backward compatibility in terms of keeping previous data
   */
  private static final String TAG_INFO = "324909sdfsd98098";

  /**
   * Key to store if the device does not support crypto
   */
  private static final String KEY_NO_CRYPTO = "dfsklj2342nasdfoasdfcrpknasdf";

  private Context context;
  private EncryptionMethod encryptionMethod;
  private String password;
  private LogLevel logLevel;
  private Storage cryptoStorage;
  private Encoder encoder;
  private Parser parser;
  private Encryption encryption;
  private Callback callback;

  public enum EncryptionMethod {
    HIGHEST, MEDIUM, NO_ENCRYPTION
  }

  public HawkBuilder(Context context) {
    this.context = context.getApplicationContext();
  }

  public HawkBuilder setEncryptionMethod(EncryptionMethod encryptionMethod) {
    this.encryptionMethod = encryptionMethod;
    return this;
  }

  public HawkBuilder setPassword(String password) {
    this.password = password;
    return this;
  }

  public HawkBuilder setLogLevel(LogLevel logLevel) {
    this.logLevel = logLevel;
    return this;
  }

  public HawkBuilder setStorage(Storage storage) {
    this.cryptoStorage = storage;
    return this;
  }

  public HawkBuilder setCallback(Callback callback) {
    this.callback = callback;
    return this;
  }

  public Context getContext() {
    return context;
  }

  public EncryptionMethod getEncryptionMethod() {
    if (encryptionMethod == null) {
      encryptionMethod = EncryptionMethod.MEDIUM;
    }
    return encryptionMethod;
  }

  public String getPassword() {
    return password;
  }

  public LogLevel getLogLevel() {
    if (logLevel == null) {
      logLevel = LogLevel.NONE;
    }
    return logLevel;
  }

  public Storage getStorage() {
    if (cryptoStorage == null) {
      cryptoStorage = new SharedPreferencesStorage(context, TAG);
    }
    return cryptoStorage;
  }

  public Encoder getEncoder() {
    if (encoder == null) {
      encoder = new HawkEncoder(getParser());
    }
    return encoder;
  }

  public Storage getInfoStorage() {
    return new SharedPreferencesStorage(context, TAG_INFO);
  }

  public Parser getParser() {
    if (parser == null) {
      parser = new GsonParser(new Gson());
    }
    return parser;
  }

  public Encryption getEncryption() {
    return encryption;
  }

  public boolean isEncrypted() {
    return encryptionMethod != EncryptionMethod.NO_ENCRYPTION;
  }

  private void validate() {
    if (getEncryptionMethod() == EncryptionMethod.HIGHEST) {
      if (TextUtils.isEmpty(getPassword())) {
        throw new IllegalStateException("Password cannot be null " +
            "if encryption mode is highest");
      }
    }
  }

  public void build() {
    if (callback != null) {
      new Handler().post(new Runnable() {
        @Override
        public void run() {
          try {
            startBuild();
            callback.onSuccess();
          } catch (Exception e) {
            callback.onFail(e);
          }
        }
      });
      return;
    }
    startBuild();
  }

  private void startBuild() {
    validate();
    setEncryption();
  }

  private void setEncryption() {
    switch (getEncryptionMethod()) {
      case NO_ENCRYPTION:
        break;
      case HIGHEST:
        encryption = new AesEncryption(getStorage(), getPassword());
        if (!getEncryption().init()) {
          getInfoStorage().put(KEY_NO_CRYPTO, true);
          encryptionMethod = EncryptionMethod.NO_ENCRYPTION;
        }
        break;
      case MEDIUM:
        encryption = new AesEncryption(getStorage(), null);
        if (!getEncryption().init()) {
          getInfoStorage().put(KEY_NO_CRYPTO, true);
          encryptionMethod = EncryptionMethod.NO_ENCRYPTION;
        }
        break;
    }
  }

  public static Storage newSharedPrefStorage(Context context) {
    return new SharedPreferencesStorage(context, TAG);
  }

  public static Storage newSqliteStorage(Context context) {
    return new SqliteStorage(context);
  }

  /**
   * Callback interface to make actions on another place and execute code
   * based on a result of action
   * onSuccess function will be called when action is successful
   * onFail function will be called when action fails due to a reason
   */
  public interface Callback {
    void onSuccess();

    void onFail(Exception e);
  }
}

@@ -22,16 +22,46 @@
 .hawk;%0A%0A
+import com.google.gson.Gson;%0A%0A
 import a
@@ -84,16 +84,16 @@
 ontext;%0A
-
 import a
@@ -150,36 +150,159 @@
 %0Aimport 
-com.google.gson.Gson
+rx.Observable;%0Aimport rx.Subscriber;%0Aimport rx.android.schedulers.AndroidSchedulers;%0Aimport rx.functions.Func0;%0Aimport rx.schedulers.Schedulers
 ;%0A%0A/**%0A 
@@ -4901,10 +4901,829 @@
 e);%0A  %7D%0A
+    public Observable%3CBoolean%3E buildRx() %7B%0A        return Observable.defer(new Func0%3CObservable%3CBoolean%3E%3E() %7B%0A            @Override%0A            public Observable%3CBoolean%3E call() %7B%0A                return Observable.create(new Observable.OnSubscribe%3CBoolean%3E() %7B%0A                    @Override%0A                    public void call(Subscriber%3C? super Boolean%3E subscriber) %7B%0A                        try %7B%0A                            startBuild();%0A                            subscriber.onNext(true);%0A                            subscriber.onCompleted();%0A                        %7D catch (Exception e) %7B%0A                            subscriber.onError(e);%0A                        %7D%0A                    %7D%0A                %7D);%0A            %7D%0A        %7D).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());%0A    %7D%0A
 %7D%0A

Add HawkBuilder.buildRx()