/**
 * This is an example of implementing an application service that will run in response to an alarm,
 * allowing us to move long duration work out of an intent receiver.
 *
 * @see AlarmService
 * @see AlarmService_Alarm
 */
public class EnvelopeService extends Service {
  NotificationManager mNM;
  private RedditSettings mSettings = new RedditSettings();
  private HttpClient mClient = Common.getGzipHttpClient();

  @Override
  public void onCreate() {
    mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mSettings.loadRedditPreferences(this, mClient);
    new PeekEnvelopeServiceTask(this, mClient, mSettings.getMailNotificationStyle()).execute();
  }

  private class PeekEnvelopeServiceTask extends PeekEnvelopeTask {
    public PeekEnvelopeServiceTask(
        Context context, HttpClient client, String mailNotificationStyle) {
      super(context, client, mailNotificationStyle);
    }

    @Override
    public void onPostExecute(Object count) {
      super.onPostExecute(count);
      EnvelopeService.this.stopSelf();
    }
  }

  @Override
  public IBinder onBind(Intent intent) {
    return mBinder;
  }

  /**
   * This is the object that receives interactions from clients. See RemoteService for a more
   * complete example.
   */
  private final IBinder mBinder =
      new Binder() {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
          return super.onTransact(code, data, reply, flags);
        }
      };

  public static void resetAlarm(Context context, long interval) {
    // Create an IntentSender that will launch our service, to be scheduled
    // with the alarm manager.
    PendingIntent alarmSender =
        PendingIntent.getService(context, 0, new Intent(context, EnvelopeService.class), 0);
    AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE);
    am.cancel(alarmSender);
    if (interval != 0)
      am.setRepeating(
          AlarmManager.ELAPSED_REALTIME_WAKEUP,
          SystemClock.elapsedRealtime(),
          interval,
          alarmSender);
  }
}
public final class PickSubredditActivity extends ListActivity {

  private static final String TAG = "PickSubredditActivity";

  // Group 1: inner
  private final Pattern MY_SUBREDDITS_OUTER =
      Pattern.compile("your front page reddits.*?<ul>(.*?)</ul>", Pattern.CASE_INSENSITIVE);
  // Group 3: subreddit name. Repeat the matcher.find() until it fails.
  private final Pattern MY_SUBREDDITS_INNER = Pattern.compile("<a(.*?)/r/(.*?)>(.+?)</a>");

  private RedditSettings mSettings = new RedditSettings();
  private HttpClient mClient = Common.getGzipHttpClient();

  private PickSubredditAdapter mSubredditsAdapter;
  private ArrayList<String> mSubredditsList;
  private static final Object ADAPTER_LOCK = new Object();
  private EditText mEt;

  private AsyncTask<?, ?, ?> mCurrentTask = null;
  private final Object mCurrentTaskLock = new Object();

  public static final String[] DEFAULT_SUBREDDITS = {
    "reddit.com",
    "pics",
    "politics",
    "wtf",
    "funny",
    "technology",
    "askreddit",
    "science",
    "programming",
    "gaming",
    "worldnews",
    "comics",
    "offbeat",
    "videos",
    "environment",
    "iama",
    "business",
    "entertainment",
    "bestof",
    "economics",
    "marijuana",
    "todayilearned",
    "linux",
    "android"
  };

  // A list of special subreddits that can be viewed, but cannot be used for submissions. They
  // inherit from the FakeSubreddit class
  // in the redditdev source, so we use the same naming here. Note: Should we add r/Random and
  // r/Friends?
  public static final String[] FAKE_SUBREDDITS = {Constants.FRONTPAGE_STRING, "all"};

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    CookieSyncManager.createInstance(getApplicationContext());

    mSettings.loadRedditPreferences(this, mClient);
    setRequestedOrientation(mSettings.getRotation());
    requestWindowFeature(Window.FEATURE_PROGRESS);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

    resetUI(null);

    mSubredditsList = cacheSubredditsList(mSubredditsList);

    if (CollectionUtils.isEmpty(mSubredditsList)) restoreLastNonConfigurationInstance();

    if (CollectionUtils.isEmpty(mSubredditsList)) {
      new DownloadRedditsTask().execute();
    } else {
      addFakeSubredditsUnlessSuppressed();
      resetUI(new PickSubredditAdapter(this, mSubredditsList));
    }
  }

  @Override
  public void onResume() {
    super.onResume();
    CookieSyncManager.getInstance().startSync();
  }

  @Override
  public void onPause() {
    super.onPause();
    CookieSyncManager.getInstance().stopSync();
  }

  @Override
  public Object onRetainNonConfigurationInstance() {
    // Avoid having to re-download and re-parse the subreddits list
    // when rotating or opening keyboard.
    return mSubredditsList;
  }

  @SuppressWarnings("unchecked")
  private void restoreLastNonConfigurationInstance() {
    mSubredditsList = (ArrayList<String>) getLastNonConfigurationInstance();
  }

  void resetUI(PickSubredditAdapter adapter) {
    setTheme(mSettings.getTheme());
    setContentView(R.layout.pick_subreddit_view);
    registerForContextMenu(getListView());

    synchronized (ADAPTER_LOCK) {
      if (adapter == null) {
        // Reset the list to be empty.
        mSubredditsList = new ArrayList<String>();
        mSubredditsAdapter = new PickSubredditAdapter(this, mSubredditsList);
      } else {
        mSubredditsAdapter = adapter;
      }
      setListAdapter(mSubredditsAdapter);
      mSubredditsAdapter.mLoading = false;
      mSubredditsAdapter.notifyDataSetChanged(); // Just in case
    }
    Common.updateListDrawables(this, mSettings.getTheme());

    // Set the EditText to do same thing as onListItemClick
    mEt = (EditText) findViewById(R.id.pick_subreddit_input);
    if (mEt != null) {
      mEt.setOnKeyListener(
          new OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
              if ((event.getAction() == KeyEvent.ACTION_DOWN)
                  && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                returnSubreddit(mEt.getText().toString().trim());
                return true;
              }
              return false;
            }
          });
      mEt.setFocusableInTouchMode(true);
    }
    Button goButton = (Button) findViewById(R.id.pick_subreddit_button);
    if (goButton != null) {
      goButton.setOnClickListener(
          new OnClickListener() {
            public void onClick(View v) {
              returnSubreddit(mEt.getText().toString().trim());
            }
          });
    }

    getListView().requestFocus();
  }

  @Override
  protected void onListItemClick(ListView l, View v, int position, long id) {
    String item = mSubredditsAdapter.getItem(position);
    returnSubreddit(item);
  }

  private void returnSubreddit(String subreddit) {
    Intent intent = new Intent();
    intent.setData(Util.createSubredditUri(subreddit));
    setResult(RESULT_OK, intent);
    finish();
  }

  private void enableLoadingScreen() {
    if (Util.isLightTheme(mSettings.getTheme())) {
      setContentView(R.layout.loading_light);
    } else {
      setContentView(R.layout.loading_dark);
    }
    synchronized (ADAPTER_LOCK) {
      if (mSubredditsAdapter != null) mSubredditsAdapter.mLoading = true;
    }
    getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 0);
  }

  private void disableLoadingScreen() {
    resetUI(mSubredditsAdapter);
    getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 10000);
  }

  class DownloadRedditsTask extends AsyncTask<Void, Void, ArrayList<String>> {
    @Override
    public ArrayList<String> doInBackground(Void... voidz) {
      ArrayList<String> reddits = null;
      HttpEntity entity = null;
      try {

        reddits = cacheSubredditsList(reddits);

        if (reddits == null) {
          reddits = new ArrayList<String>();

          HttpGet request = new HttpGet(Constants.REDDIT_BASE_URL + "/reddits");
          // Set timeout to 15 seconds
          HttpParams params = request.getParams();
          HttpConnectionParams.setConnectionTimeout(params, 15000);
          HttpConnectionParams.setSoTimeout(params, 15000);

          HttpResponse response = mClient.execute(request);
          entity = response.getEntity();
          BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));

          String line = in.readLine();
          in.close();
          entity.consumeContent();

          Matcher outer = MY_SUBREDDITS_OUTER.matcher(line);
          if (outer.find()) {
            Matcher inner = MY_SUBREDDITS_INNER.matcher(outer.group(1));
            while (inner.find()) {
              reddits.add(inner.group(3));
            }
          } else {
            return null;
          }

          if (Constants.LOGGING) Log.d(TAG, "new subreddit list size: " + reddits.size());

          if (Constants.USE_SUBREDDITS_CACHE) {
            try {
              CacheInfo.setCachedSubredditList(getApplicationContext(), reddits);
              if (Constants.LOGGING) Log.d(TAG, "wrote subreddit list to cache:" + reddits);
            } catch (IOException e) {
              if (Constants.LOGGING) Log.e(TAG, "error on setCachedSubredditList", e);
            }
          }
        }

        return reddits;

      } catch (Exception e) {
        if (Constants.LOGGING) Log.e(TAG, "failed", e);
        if (entity != null) {
          try {
            entity.consumeContent();
          } catch (Exception e2) {
            // Ignore.
          }
        }
      }
      return null;
    }

    @Override
    public void onPreExecute() {
      synchronized (mCurrentTaskLock) {
        if (mCurrentTask != null) {
          this.cancel(true);
          return;
        }
        mCurrentTask = this;
      }
      resetUI(null);
      enableLoadingScreen();
    }

    @Override
    public void onPostExecute(ArrayList<String> reddits) {
      synchronized (mCurrentTaskLock) {
        mCurrentTask = null;
      }
      disableLoadingScreen();

      if (reddits == null || reddits.size() == 0) {
        // Need to make a copy because Arrays.asList returns List backed by original array
        mSubredditsList = new ArrayList<String>();
        mSubredditsList.addAll(Arrays.asList(DEFAULT_SUBREDDITS));
      } else {
        mSubredditsList = reddits;
      }
      addFakeSubredditsUnlessSuppressed();
      resetUI(new PickSubredditAdapter(PickSubredditActivity.this, mSubredditsList));
    }
  }

  private void addFakeSubredditsUnlessSuppressed() {
    // Insert special reddits (front page, all) into subreddits list, unless suppressed by Intent
    // extras
    Bundle extras = getIntent().getExtras();
    boolean addFakeSubreddits = false;
    if (extras != null) {
      boolean shouldHideFakeSubreddits =
          extras.getBoolean(Constants.EXTRA_HIDE_FAKE_SUBREDDITS_STRING, false);
      if (!shouldHideFakeSubreddits) {
        addFakeSubreddits = true;
      }
    } else {
      addFakeSubreddits = true;
    }
    if (addFakeSubreddits) {
      mSubredditsList.addAll(0, Arrays.asList(FAKE_SUBREDDITS));
    }
  }

  private final class PickSubredditAdapter extends ArrayAdapter<String> {
    private LayoutInflater mInflater;
    private boolean mLoading = true;
    private int mFrequentSeparatorPos = ListView.INVALID_POSITION;

    public PickSubredditAdapter(Context context, List<String> objects) {
      super(context, 0, objects);

      mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public boolean isEmpty() {
      if (mLoading) {
        // We don't want the empty state to show when loading.
        return false;
      } else {
        return super.isEmpty();
      }
    }

    @Override
    public int getItemViewType(int position) {
      if (position == mFrequentSeparatorPos) {
        // We don't want the separator view to be recycled.
        return IGNORE_ITEM_VIEW_TYPE;
      }
      return super.getItemViewType(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View view;

      // Here view may be passed in for re-use, or we make a new one.
      if (convertView == null) {
        view = mInflater.inflate(android.R.layout.simple_list_item_1, null);
      } else {
        view = convertView;
      }

      TextView text = (TextView) view.findViewById(android.R.id.text1);
      text.setText(mSubredditsAdapter.getItem(position));

      return view;
    }
  }

  protected Dialog onCreateDialog(int id) {
    Dialog dialog;
    ProgressDialog pdialog;

    switch (id) {
        // "Please wait"
      case Constants.DIALOG_LOADING_REDDITS_LIST:
        pdialog = new ProgressDialog(this);
        pdialog.setMessage("Loading your reddits...");
        pdialog.setIndeterminate(true);
        pdialog.setCancelable(false);
        dialog = pdialog;
        break;
      default:
        throw new IllegalArgumentException("Unexpected dialog id " + id);
    }
    return dialog;
  }

  @Override
  protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    final int[] myDialogs = {
      Constants.DIALOG_LOADING_REDDITS_LIST,
    };
    for (int dialog : myDialogs) {
      try {
        dismissDialog(dialog);
      } catch (IllegalArgumentException e) {
        // Ignore.
      }
    }
  }

  protected ArrayList<String> cacheSubredditsList(ArrayList<String> reddits) {
    if (Constants.USE_SUBREDDITS_CACHE) {
      if (CacheInfo.checkFreshSubredditListCache(getApplicationContext())) {
        reddits = CacheInfo.getCachedSubredditList(getApplicationContext());
        if (Constants.LOGGING) Log.d(TAG, "cached subreddit list:" + reddits);
      }
    }
    return reddits;
  }
}