package com.dbs.consent.demo.customView;

/*
 * Author: Felipe Herranz (felhr85@gmail.com)
 * Contributors:Francesco Verheye (verheye.francesco@gmail.com)
 * 		Israel Dominguez (dominguez.israel@gmail.com)
 */

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener {
  private static final int CLEAR_FOCUS = 0;

  private ViewGroup layout;
  private int layoutBottom;
  private InputMethodManager im;
  private int[] coords;
  private boolean isKeyboardShow;
  private SoftKeyboardChangesThread softKeyboardThread;
  private List<EditText> editTextList;

  private View tempView; // reference to a focused EditText

  public SoftKeyboard(ViewGroup layout, InputMethodManager im) {
    this.layout = layout;
    keyboardHideByDefault();
    initEditTexts(layout);
    this.im = im;
    this.coords = new int[2];
    this.isKeyboardShow = false;
    this.softKeyboardThread = new SoftKeyboardChangesThread();
    this.softKeyboardThread.start();
  }


  public void openSoftKeyboard() {
    if (!isKeyboardShow) {
      layoutBottom = getLayoutCoordinates();
      im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
      softKeyboardThread.keyboardOpened();
      isKeyboardShow = true;
    }
  }

  public void closeSoftKeyboard() {
    if (isKeyboardShow) {
      im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
      isKeyboardShow = false;
    }
  }

  public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback) {
    softKeyboardThread.setCallback(mCallback);
  }

  public void unRegisterSoftKeyboardCallback() {
    softKeyboardThread.stopThread();
  }

  public interface SoftKeyboardChanged {
    public void onSoftKeyboardHide();

    public void onSoftKeyboardShow();
  }

  private int getLayoutCoordinates() {
    layout.getLocationOnScreen(coords);
    return coords[1] + layout.getHeight();
  }

  private void keyboardHideByDefault() {
    layout.setFocusable(true);
    layout.setFocusableInTouchMode(true);
  }

  /*
   * InitEditTexts now handles EditTexts in nested views
   * Thanks to Francesco Verheye (verheye.francesco@gmail.com)
   */
  private void initEditTexts(ViewGroup viewgroup) {
    if (editTextList == null)
      editTextList = new ArrayList<EditText>();

    int childCount = viewgroup.getChildCount();
    for (int i = 0; i <= childCount - 1; i++) {
      View v = viewgroup.getChildAt(i);

      if (v instanceof ViewGroup) {
        initEditTexts((ViewGroup) v);
      }

      if (v instanceof EditText) {
        EditText editText = (EditText) v;
        editText.setOnFocusChangeListener(this);
        editText.setCursorVisible(true);
        editTextList.add(editText);
      }
    }
  }

  /*
   * OnFocusChange does update tempView correctly now when keyboard is still shown
   * Thanks to Israel Dominguez (dominguez.israel@gmail.com)
   */
  @Override
  public void onFocusChange(View v, boolean hasFocus) {
    if (hasFocus) {
      tempView = v;
      if (!isKeyboardShow) {
        layoutBottom = getLayoutCoordinates();
        softKeyboardThread.keyboardOpened();
        isKeyboardShow = true;
      }
    }
  }

  // This handler will clear focus of selected EditText
  private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message m) {
      switch (m.what) {
        case CLEAR_FOCUS:
          if (tempView != null) {
            tempView.clearFocus();
            tempView = null;
          }
          break;
      }
    }
  };

  private class SoftKeyboardChangesThread extends Thread {
    private AtomicBoolean started;
    private SoftKeyboardChanged mCallback;

    public SoftKeyboardChangesThread() {
      started = new AtomicBoolean(true);
    }

    public void setCallback(SoftKeyboardChanged mCallback) {
      this.mCallback = mCallback;
    }

    @Override
    public void run() {
      while (started.get()) {
        // Wait until keyboard is requested to open
        synchronized (this) {
          try {
            wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        int currentBottomLocation = getLayoutCoordinates();

        // There is some lag between open soft-keyboard function and when it really appears.
        while (currentBottomLocation == layoutBottom && started.get()) {
          currentBottomLocation = getLayoutCoordinates();
        }

        if (started.get())
          mCallback.onSoftKeyboardShow();

        // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
        // and at some moment equals layoutBottom.
        // That broke the previous logic, so I added this new loop to handle this.
        while (currentBottomLocation >= layoutBottom && started.get()) {
          currentBottomLocation = getLayoutCoordinates();
        }

        // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
        while (currentBottomLocation != layoutBottom && started.get()) {
          synchronized (this) {
            try {
              wait(500);
            } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
          }
          currentBottomLocation = getLayoutCoordinates();
        }

        if (started.get())
          mCallback.onSoftKeyboardHide();

        // if keyboard has been opened clicking and EditText.
        if (isKeyboardShow && started.get())
          isKeyboardShow = false;

        // if an EditText is focused, remove its focus (on UI thread)
        if (started.get())
          mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
      }
    }

    public void keyboardOpened() {
      synchronized (this) {
        notify();
      }
    }

    public void stopThread() {
      synchronized (this) {
        started.set(false);
        notify();
      }
    }

  }
}